package com.zzj.aop;

import com.zzj.annotation.RateLimitAnnotation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

@Component
@Aspect
@Slf4j
public class RateLimitAop {

    // 存储不同方法对应的时间窗口
    private final Map<String, TimeWindow> timeWindows = new HashMap<>();

    // 定义一个切入点，用于匹配所有被@RateLimitAnnotation注解的方法
    @Pointcut("@annotation(com.zzj.annotation.RateLimitAnnotation)")
    public void rateLimitPointcut() {
    }

    // 在切入点方法周围织入增强逻辑
    @Around("rateLimitPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 获取被@RateLimitAnnotation注解的限流信息
        RateLimitAnnotation rateLimit = getRateLimitAnnotation(joinPoint);
        int limit = rateLimit.limit(); // 获取限制次数
        int duration = rateLimit.duration(); // 获取时间间隔

        // 获取当前被调用方法的签名（方法名、参数等）
        String methodKey = joinPoint.getSignature().toShortString();

        // 根据方法签名查找对应的时间窗口对象，如果不存在则新建一个
        TimeWindow timeWindow = timeWindows.computeIfAbsent(methodKey, k -> new TimeWindow(duration));

        // 判断是否允许继续执行方法调用
        if (timeWindow.allowRequest(limit)) {
            // 允许继续执行方法调用
            return joinPoint.proceed();
        } else {
            // 超过限流阈值，抛出异常
            throw new RuntimeException("请求过于频繁，请稍后再试！");
        }
    }

    // 获取被@RateLimitAnnotation注解的限流信息
    private RateLimitAnnotation getRateLimitAnnotation(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        return method.getAnnotation(RateLimitAnnotation.class);
    }

    // 内部静态类，用于表示时间窗口
    private static class TimeWindow {
        private final long duration; // 时间窗口的持续时间
        private long start; // 时间窗口的起始时间
        private int count; // 时间窗口内方法调用次数

        public TimeWindow(long duration) {
            this.duration = duration;
            this.start = System.currentTimeMillis();
            this.count = 0;
        }

        // 判断是否允许请求
        public synchronized boolean allowRequest(int limit) {
            long now = System.currentTimeMillis();
            if (now - start < duration) {
                // 在时间窗口内，检查调用次数是否超过限制
                count++;
                return count <= limit;
            } else {
                // 超过时间窗口，重置时间窗口和调用次数
                start = now;
                count = 1;
                return true;
            }
        }
    }
}
